#include "./BSP/KEY/key_manager.h"
#include "./BSP/LED/led_manager.h"
#include "stdio.h"

/* -------------------------------------------- 按键状态管理 ---------------------------------------------- */

/* 按键状态枚举定义 */
typedef enum
{
    KEY_STATE_IDLE = 0,     /* 空闲状态 */
    KEY_STATE_COMBO,        /* 组合键激活状态 */
    KEY_STATE_PARTIAL       /* 部分释放状态 */
} KeyState_e;

/* 组合键状态管理结构 */
typedef struct
{
    KeyState_e state;       /* 状态：0=空闲，1=激活，2=部分释放 */
    uint8_t key1_mask;      /* 第一个按键的掩码 */
    uint8_t key2_mask;      /* 第二个按键的掩码 */
    uint8_t combo_id;       /* 组合键ID */
} ComboKeyState_t;

/* 组合键状态数组 */
static ComboKeyState_t sg_comboKeys[] = {
    {KEY_STATE_IDLE, KEY_CODE_0, KEY_CODE_1, KEY0_1},    /* 0+1组合键 */
    {KEY_STATE_IDLE, KEY_CODE_0, KEY_CODE_2, KEY0_2},    /* 0+2组合键 */
    /* 可以在这里添加更多组合键 */
};

/* 组合键数量 */
#define COMBO_KEY_COUNT (sizeof(sg_comboKeys) / sizeof(sg_comboKeys[0]))

/* 状态缓存变量 */
static uint8_t sg_lastKeyStates = 0;
static bool sg_comboKeyActive = false;

/**
 * @brief  更新组合键状态
 * @param  sysTime 当前系统时间，单位毫秒
 */
static void UpdateComboKeyState(uint32_t sysTime)
{
    uint8_t keyStates = GPIO_GetKEY();  /* 一次性获取所有按键状态 */
    
    /* 只在按键状态发生变化时更新 */
    if (keyStates != sg_lastKeyStates)
    {
        sg_comboKeyActive = false;  /* 重置组合键活动标志 */
        
        /* 遍历所有组合键，更新状态 */
        for (int i = 0; i < COMBO_KEY_COUNT; i++)
        {
            ComboKeyState_t *pCombo = &sg_comboKeys[i];
            
            /* 使用位运算直接检查按键状态 */
            bool key1_pressed = (keyStates & pCombo->key1_mask) != 0;
            bool key2_pressed = (keyStates & pCombo->key2_mask) != 0;
            
            /* 更新组合键状态 */
            if (key1_pressed && key2_pressed)
            {
                /* 两个按键同时按下，激活组合键状态 */
                pCombo->state = KEY_STATE_COMBO;
                sg_comboKeyActive = true;
            }
            else if (!key1_pressed && !key2_pressed)
            {
                /* 两个按键都释放，回到空闲状态 */
                pCombo->state = KEY_STATE_IDLE;
            }
            else if (pCombo->state == KEY_STATE_COMBO)
            {
                /* 只有一个按键松开，部分释放状态 */
                pCombo->state = KEY_STATE_PARTIAL;
                sg_comboKeyActive = true;
            }
            
            /* 更新全局组合键活动标志 */
            if (pCombo->state != KEY_STATE_IDLE)
            {
                sg_comboKeyActive = true;
            }
        }
        
        sg_lastKeyStates = keyStates;  /* 更新状态缓存 */
    }
}

/**
 * @brief  获取指定组合键的状态
 * @param  comboId 组合键ID
 * @retval 返回组合键状态，0=未激活，1=激活，2=部分释放
 */
static uint8_t GetComboKeyState(uint8_t comboId)
{
    for (int i = 0; i < COMBO_KEY_COUNT; i++)
    {
        if (sg_comboKeys[i].combo_id == comboId)
        {
            return sg_comboKeys[i].state;
        }
    }
    return KEY_STATE_IDLE; /* 未找到对应的组合键 */
}

/**
 * @brief  获取组合键状态
 * @param  无
 * @retval 返回组合键是否处于活动状态
 */
bool FML_KEY_IsComboActive(void)
{
    /* 直接返回缓存的组合键活动状态 */
    return sg_comboKeyActive;
}

/* -------------------------------------------- 按键IO初始化 ---------------------------------------------- */

static void InitKeyIo(void)
{ 
    GPIO_InitTypeDef gpio_init_struct;
    
    /* 开启时钟 */
    KEY0_GPIO_CLK_ENABLE();                                     /* KEY0时钟使能 */
    KEY1_GPIO_CLK_ENABLE();                                     /* KEY1时钟使能 */
    KEY2_GPIO_CLK_ENABLE();                                     /* KEY2时钟使能 */

    /* GPIO初始化 */
    gpio_init_struct.Pin = KEY0_GPIO_PIN;                       /* KEY0引脚 */
    gpio_init_struct.Mode = GPIO_MODE_INPUT;                    /* 输入 */
    gpio_init_struct.Pull = GPIO_PULLUP;                        /* 上拉 */
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;              /* 高速 */
    HAL_GPIO_Init(KEY0_GPIO_PORT, &gpio_init_struct);           /* KEY0引脚模式设置,上拉输入 */

    gpio_init_struct.Pin = KEY1_GPIO_PIN;                       /* KEY1引脚 */
    gpio_init_struct.Mode = GPIO_MODE_INPUT;                    /* 输入 */
    gpio_init_struct.Pull = GPIO_PULLUP;                        /* 上拉 */
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;              /* 高速 */
    HAL_GPIO_Init(KEY1_GPIO_PORT, &gpio_init_struct);           /* KEY1引脚模式设置,上拉输入 */

    gpio_init_struct.Pin = KEY2_GPIO_PIN;                       /* KEY2引脚 */
    gpio_init_struct.Mode = GPIO_MODE_INPUT;                    /* 输入 */
    gpio_init_struct.Pull = GPIO_PULLDOWN;                      /* 下拉 */
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;              /* 高速 */
    HAL_GPIO_Init(KEY2_GPIO_PORT, &gpio_init_struct);           /* KEY2引脚模式设置,下拉输入 */
}

/* -------------------------------------------- 按键状态读取函数 ------------------------------------------- */

cotKeyState_e ReadKey0(void)
{
    /* 快速检查组合键状态 */
    if (sg_comboKeyActive)
    {
        return COT_KEY_OFF;
    }
    
    return (GPIO_GetKEY() & KEY_CODE_0) ? COT_KEY_ON : COT_KEY_OFF;
}

cotKeyState_e ReadKey1(void)
{
    /* 快速检查组合键状态 */
    if (sg_comboKeyActive)
    {
        return COT_KEY_OFF;
    }

    return (GPIO_GetKEY() & KEY_CODE_1) ? COT_KEY_ON : COT_KEY_OFF;
}

cotKeyState_e ReadKey2(void)
{
    /* 快速检查组合键状态 */
    if (sg_comboKeyActive)
    {
        return COT_KEY_OFF;
    }
    
    return (GPIO_GetKEY() & KEY_CODE_2) ? COT_KEY_ON : COT_KEY_OFF;
}

/* 组合键检测函数 */
cotKeyState_e ReadKey0_1(void)
{
    /* 使用辅助函数检查0+1组合键状态 */
    return (GetComboKeyState(KEY0_1) == KEY_STATE_COMBO) ? COT_KEY_ON : COT_KEY_OFF;
}

/* UP+SET组合键检测函数 */
cotKeyState_e ReadKey0_2(void)
{
    /* 使用辅助函数检查UP+SET组合键状态 */
    return (GetComboKeyState(KEY0_2) == KEY_STATE_COMBO) ? COT_KEY_ON : COT_KEY_OFF;
}

/* -------------------------------------------- 按键回调函数 ---------------------------------------------- */

/* KEY0键回调函数 */
static void OnKey0Press(cotKey_t key)
{
    /* 如果任何组合键处于活动状态，不处理单键事件 */
    if (FML_KEY_IsComboActive())
    {
        return;
    }
    
    if (!cotKey_IsLongPress(key))   /* 判断是否触发了长按 */
    {
        if (cotKey_IsTriggerWayMet(key, COT_KEY_TRIGGER_WAY_PRESS))     /* 按下首次触发 */
        {
            printf("KEY0 pressed\r\n");
        }

        if (cotKey_IsTriggerWayMet(key, COT_KEY_TRIGGER_WAY_LOSSEN))    /* 识别到短按 */
        {
            cotLed_Toggle(LED_0);
            printf("KEY0 released\r\n");
        }
    }
    else
    {
        if (cotKey_IsTriggerWayMet(key, COT_KEY_TRIGGER_WAY_PRESS))     /* 识别到长按 */
        {
            cotLed_Toggle(LED_0);
            printf("KEY0 long press detected\r\n");
        }

        if (cotKey_IsTriggerWayMet(key, COT_KEY_TRIGGER_WAY_LOSSEN))    /* 长按松开 */
        {
            printf("KEY0 long press released\r\n");  
        }
    }
}

/* KEY1键回调函数 */
static void OnKey1Press(cotKey_t key)
{
    /* 如果任何组合键处于活动状态，不处理单键事件 */
    if (FML_KEY_IsComboActive())
    {
        return;
    }
    
    uint8_t clickCount = cotKey_GetClickCount(key);
    switch (clickCount)
    {
        case 1:  /* 单击 */
            cotLed_Toggle(LED_0);
            printf("Key1 single click\r\n");
            break;
        case 2:  /* 双击 */
            cotLed_TwinkleWithCount(LED_0, 200, 2, COT_LED_OFF);
            printf("Key1 double click\r\n");
            break;
        case 3:  /* 三击 */
            cotLed_TwinkleWithCount(LED_0, 200, 3, COT_LED_OFF);
            printf("Key1 triple click\r\n");
            break;
        case 4:  /* 四击 */
            cotLed_TwinkleWithCount(LED_0, 200, 4, COT_LED_OFF);
            printf("Key1 quadruple click\r\n");
            break;
        default:
            if (clickCount > 4)
            {
                cotLed_TwinkleWithCount(LED_0, 200, clickCount, COT_LED_OFF);
                printf("Key1 clicked %d times\r\n", clickCount);
            }
            break;
    }
}

/* KEY2键回调函数 */
static void OnKey2Press(cotKey_t key)
{
    /* 如果任何组合键处于活动状态，不处理单键事件 */
    if (FML_KEY_IsComboActive())
    {
        return;
    }
    
    /* 连击复合模式：按下时间超过阈值时变为普通模式，否则为连击模式 */
    uint8_t clickCount = cotKey_GetClickCount(key);
    
    if (clickCount > 0)
    {
        /* 连击模式 - 按下时间在阈值内 */
        switch (clickCount)
        {
            case 1:  /* 单击 */
                cotLed_Toggle(LED_1);
                printf("Key2 single click\r\n");
                break;
            case 2:  /* 双击 */
                cotLed_TwinkleWithCount(LED_1, 200, 2, COT_LED_OFF);
                printf("Key2 double click\r\n");
                break;
            case 3:  /* 三击 */
                cotLed_TwinkleWithCount(LED_1, 200, 3, COT_LED_OFF);
                printf("Key2 triple click\r\n");
                break;
            case 4:  /* 四击 */
                cotLed_TwinkleWithCount(LED_1, 200, 4, COT_LED_OFF);
                printf("Key2 quadruple click\r\n");
                break;
            default:
                if (clickCount > 4)
                {
                    cotLed_TwinkleWithCount(LED_1, 200, clickCount, COT_LED_OFF);
                    printf("Key2 clicked %d times\r\n", clickCount);
                }
                break;
        }
    }
    else
    {
        /* 普通模式 - 按下时间超过阈值 */
        if (cotKey_IsTriggerWayMet(key, COT_KEY_TRIGGER_WAY_LOSSEN))    /* 松开触发 */
        {
            cotLed_TwinkleWithCount(LED_1, 100, 10, COT_LED_OFF);   /* 快速闪烁10次表示长按 */
            printf("Key2 long press (>1000ms)\r\n");
        }
    }
}

/* 组合键回调函数 */
static void OnKey0_1Press(cotKey_t key)
{
    if (!cotKey_IsLongPress(key))   /* 判断是否触发了长按 */
    {
        if (cotKey_IsTriggerWayMet(key, COT_KEY_TRIGGER_WAY_PRESS))     /* 按下首次触发 */
        {
            printf("Key0_1 combo pressed\r\n");
        }

        if (cotKey_IsTriggerWayMet(key, COT_KEY_TRIGGER_WAY_LOSSEN))    /* 识别到短按 */
        {
             cotLed_Toggle(LED_1);
             printf("Key0_1 combo released\r\n");
        }
    }
    else
    {
        if (cotKey_IsTriggerWayMet(key, COT_KEY_TRIGGER_WAY_PRESS))     /* 识别到长按 */
        {
            cotLed_Toggle(LED_1);
            printf("Key0_1 combo long press detected\r\n");
        }

        if (cotKey_IsTriggerWayMet(key, COT_KEY_TRIGGER_WAY_LOSSEN))    /* 长按松开 */
        {
            printf("Key0_1 combo long press released\r\n");
        }
    }
}

static void OnKey0_2Press(cotKey_t key)
{
    if (!cotKey_IsLongPress(key))   /* 判断是否触发了长按 */
    {
        if (cotKey_IsTriggerWayMet(key, COT_KEY_TRIGGER_WAY_PRESS))     /* 按下首次触发 */
        {
            printf("Key0_2 combo pressed\r\n");
        }

        if (cotKey_IsTriggerWayMet(key, COT_KEY_TRIGGER_WAY_LOSSEN))    /* 识别到短按 */
        {
             cotLed_Toggle(LED_1);
             printf("Key0_2 combo released\r\n");
        }
    }
    else
    {
        if (cotKey_IsTriggerWayMet(key, COT_KEY_TRIGGER_WAY_PRESS))     /* 识别到长按 */
        {
            cotLed_Toggle(LED_1);
            printf("Key0_2 combo long press detected\r\n");
        }

        if (cotKey_IsTriggerWayMet(key, COT_KEY_TRIGGER_WAY_LOSSEN))    /* 长按松开 */
        {
            printf("Key0_2 combo long press released\r\n");
        }
    }
}

/* -------------------------------------------- 按键模块API函数 ------------------------------------------- */

/**
 * @brief  按键初始化函数
 * @param  无
 * @retval 无
 */
void FML_KEY_Init(void)
{
    static cotKeyCfg_t s_keyTable[KEY_MAX_NUM] =
    {
        {.pfnKeyState = ReadKey0},
        {.pfnKeyState = ReadKey1},
        {.pfnKeyState = ReadKey2},
        {.pfnKeyState = ReadKey0_1},
        {.pfnKeyState = ReadKey0_2},
    };

    /* 初始化按键IO */
    InitKeyIo();
    
    /* 初始化组合键状态数组 */
    for (int i = 0; i < COMBO_KEY_COUNT; i++)
    {
        sg_comboKeys[i].state = KEY_STATE_IDLE;
    }

    /* 初始化COT按键模块 */
    cotKey_Init(s_keyTable, KEY_MAX_NUM);
    
    /* 注册按键回调 */
    cotKey_Listen(KEY0, OnKey0Press);
    cotKey_Listen(KEY1, OnKey1Press);
    cotKey_Listen(KEY2, OnKey2Press);
    cotKey_Listen(KEY0_1, OnKey0_1Press);
    cotKey_Listen(KEY0_2, OnKey0_2Press);

    /* 配置按键模式 */
    cotKey_ConfigLongPressMode(KEY0, 1500, 500);    /* KEY0配置为长按模式，1500ms长按，500ms重复触发 */
    cotKey_ConfigClickMode(KEY1, 200);              /* KEY1配置为连击模式，200ms连击间隔 */
    cotKey_ConfigClickCompMode(KEY2, 200, 1000, COT_KEY_TRIGGER_WAY_LOSSEN);   /* KEY2配置为连击复合模式，200ms连击间隔，1000ms超时，松开触发 */
    cotKey_ConfigLongPressMode(KEY0_1, 1000, 500);  /* KEY0+KEY1配置为长按模式，1500ms长按，500ms重复触发 */
    cotKey_ConfigLongPressMode(KEY0_2, 1000, 500);  /* KEY0+KEY2配置为长按模式，1500ms长按，500ms重复触发 */
}

/**
 * @brief  按键扫描处理函数，需要周期调用
 * @param  sysTime 当前系统时间，单位毫秒
 * @retval 无
 */
void FML_KEY_Scan(uint32_t sysTime)
{
    /* 更新组合键状态 */
    UpdateComboKeyState(sysTime);
    
    /* 调用COT按键扫描函数 */
    cotKey_Scan(sysTime);
}
